//
// Created by konglinglong on 2020/9/26.
//

#include "../include/l3IntfApi.h"

S_RanL2L3IntfContext *gpRanL2L3IntfContext = NULL;

int32_t RAN_L2_L3I_Init(void)
{
    int32_t rc;
    uint16_t l3L2UdpPort;
    uint16_t l2L3UdpPort;

    gpRanL2L3IntfContext = OS_MemCalloc(1, sizeof(S_RanL2L3IntfContext));
    IF_RETURN(==, NULL, gpRanL2L3IntfContext, O5G_RC_ER, "RAN_L2_L3I_Init() OS_MemCalloc() error!\n");

    LIST_Init(&gpRanL2L3IntfContext->msgArcObjList);

    gpRanL2L3IntfContext->pMsgArcObjPool = MP_Create(2048, sizeof(S_MsgArcObj), MEM_POOL_TYPE_NONE);
    IF_RETURN(==, NULL, gpRanL2L3IntfContext->pMsgArcObjPool, O5G_RC_ER, "RAN_L2_L3I_Init() MP_Create() error!\n");

    rc = OS_UdpInit(&gpRanL2L3IntfContext->l3SockFd);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_L3I_Init() OS_UdpInit(l3SockFd) error!\n");

    rc = OS_SelectInit(&gpRanL2L3IntfContext->selector, NULL);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_L3I_Init() OS_SelectInit() error!\n");

    gpRanL2L3IntfContext->poller = POLLER_Create(POLL_FD_NUM_MAX);
    IF_RETURN(==, NULL, gpRanL2L3IntfContext->poller, O5G_RC_ER, "RAN_L2_L3I_Init() POLLER_Create() error!\n");

    if (APP_TYPE_RAN == gpRanContext->appType)
    {
        l3L2UdpPort = RAN_L3_2_L2_UDP_PORT;
        l2L3UdpPort = RAN_L2_2_L3_UDP_PORT;
    }
    if (APP_TYPE_RAN_CU == gpRanContext->appType)
    {
        l3L2UdpPort = RAN_L3_2_L2_CU_UDP_PORT;
        l2L3UdpPort = RAN_L2_CU_2_L3_UDP_PORT;
    }
    if (APP_TYPE_RAN_DU == gpRanContext->appType)
    {
        l3L2UdpPort = RAN_L3_2_L2_DU_UDP_PORT;
        l2L3UdpPort = RAN_L2_DU_2_L3_UDP_PORT;
    }

    rc = OS_UdpBind(gpRanL2L3IntfContext->l3SockFd, "127.0.0.1", l3L2UdpPort);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_L3I_Init() OS_UdpBind(l3SockFd) error!\n");

    rc = OS_UdpConnect(gpRanL2L3IntfContext->l3SockFd, "127.0.0.1", l2L3UdpPort);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_L3I_Init() OS_UdpConnect(l3SockFd) error!\n");

    CSP_LOG_INF(RAN_MD_L2_L3I, "RAN_L2_L3I_Init() init socket success. fd[%d], RAN_L2_2_L3_UDP_PORT[%d], RAN_L3_2_L2_UDP_PORT[%d]\n",
        gpRanL2L3IntfContext->l3SockFd, l2L3UdpPort, l3L2UdpPort);

    return O5G_RC_OK;
}

int32_t RAN_L2_L3I_Final(void)
{
    int32_t rc;

    rc = OS_UdpFinal(gpRanL2L3IntfContext->l3SockFd);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_L3I_Final() OS_UdpFinal(l3SockFd) error!\n");

    rc = OS_SelectFinal(&gpRanL2L3IntfContext->selector);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_L3I_Final() OS_SelectFinal() error!\n");

    POLLER_Destroy(gpRanL2L3IntfContext->poller);

    LIST_Final(&gpRanL2L3IntfContext->msgArcObjList);

    MP_Destroy(gpRanL2L3IntfContext->pMsgArcObjPool);

    OS_MemFree(gpRanL2L3IntfContext);

    return O5G_RC_OK;
}

void RAN_L2_L3I_ProcL3L2ConfigReq(S_RanCommMsgHdr *pCommMsgHdr)
{
    S_RanL2L3MsgConfigRsp configRsp;

    configRsp.commMsgHdr.type = RAN_L2_L3_MSG_CONFIG_RSP;
    configRsp.commMsgHdr.size = sizeof(configRsp);
    configRsp.nbIdx = ((S_RanL3L2MsgConfigReq *)pCommMsgHdr)->nbIdx;
    configRsp.cellIdx = ((S_RanL3L2MsgConfigReq *)pCommMsgHdr)->cellIdx;
    send(gpRanL2L3IntfContext->l3SockFd, &configRsp, sizeof(configRsp), MSG_DONTWAIT);
}

void RAN_L2_L3I_ProcL3L2StartReq(S_RanCommMsgHdr *pCommMsgHdr)
{
    S_RanL2CellInst *pCellInst;
    S_RanL3L2MsgStartReq *pStartReq;
    S_RanL2L3MsgStartRsp startRsp;

    pStartReq = (S_RanL3L2MsgStartReq *)pCommMsgHdr;
    pCellInst = &gpRanL2Context->nbInst[pStartReq->nbIdx].cellInst[pStartReq->cellIdx];
    pCellInst->appState = APP_STATE_RUNNING;

    startRsp.commMsgHdr.type = RAN_L2_L3_MSG_START_RSP;
    startRsp.commMsgHdr.size = sizeof(startRsp);
    startRsp.nbIdx = ((S_RanL3L2MsgStartReq *)pCommMsgHdr)->nbIdx;
    startRsp.cellIdx = ((S_RanL3L2MsgStartReq *)pCommMsgHdr)->cellIdx;
    send(gpRanL2L3IntfContext->l3SockFd, &startRsp, sizeof(startRsp), MSG_DONTWAIT);
}

void RAN_L2_L3I_ProcL3L2MibTxReq(S_RanCommMsgHdr *pCommMsgHdr)
{
    S_RanL2CellInst *pCellInst;
    S_RanL3L2MsgMibTxReq *pMibTxReq;
    S_RanL2L3MsgMibTxRsp mibTxRsp;

    pMibTxReq = (S_RanL3L2MsgMibTxReq *)pCommMsgHdr;

    mibTxRsp.commMsgHdr.type = RAN_L3_L2_MSG_MIB_TX_RSP;
    mibTxRsp.commMsgHdr.size = sizeof(mibTxRsp);
    mibTxRsp.nbIdx = pMibTxReq->nbIdx;
    mibTxRsp.cellIdx = pMibTxReq->cellIdx;

    pCellInst = &gpRanL2Context->nbInst[pMibTxReq->nbIdx].cellInst[pMibTxReq->cellIdx];
    memcpy(pCellInst->mib, pMibTxReq->mib, pMibTxReq->mibSize);
    pCellInst->mibSize = pMibTxReq->mibSize;

    send(gpRanL2L3IntfContext->l3SockFd, &mibTxRsp, sizeof(mibTxRsp), MSG_DONTWAIT);
}

void RAN_L2_L3I_ProcL1ApiRspMsg()
{
    uint32_t size;
    S_RanCommMsgHdr *pCommMsgHdr;

    while (1)
    {
        pCommMsgHdr = VarRB_ReadRequest(gpRanL2Context->pL1iL3iMsgQue, &size);
        IF_BREAK(==, NULL, pCommMsgHdr, NO_LOG_MESSAGE);

        send(gpRanL2L3IntfContext->l3SockFd, pCommMsgHdr, pCommMsgHdr->size, MSG_DONTWAIT);
        VarRB_ReadConsume(gpRanL2Context->pL1iL3iMsgQue, pCommMsgHdr);
    }
}

void RAN_L2_L3I_IndProcess(int32_t event, int fd, void *data)
{
    int32_t rc;

    S_OsSelectInd indMsg;
    rc = OS_SelectRecvByFd(fd, &indMsg);
    IF_RETURN(!=, O5G_RC_OK, rc, NO_RETURN_VALUE, "RAN_L2_L3I_IndProcess() OS_SelectRecv() error!\n");

    switch (indMsg.ind)
    {
    case RAN_L2_INTRA_IND_MSG_L1_RSP_MSG:
        RAN_L2_L3I_ProcL1ApiRspMsg();
        break;
    default:
        CSP_LOG_WRN(RAN_MD_L2_L3I, "RAN_L2_L3I_IndProcess() recv unsupport ind[%#x]\n", indMsg.ind);
        break;
    }
}

void RAN_L2_L3I_MsgProcess(int32_t event, int fd, void *data)
{
    int32_t rc;
    S_RanCommMsgHdr *pCommMsgHdr;

    rc = recv(fd, gpRanL2L3IntfContext->sockRecvBuffer, SOCK_BUFFER_SIZE, MSG_DONTWAIT);
    IF_RETURN(>, (int32_t)sizeof(S_RanCommMsgHdr), rc, NO_RETURN_VALUE, "RAN_L2_L3I_MsgProcess() recv() error! rc[%d] < %u\n",
        rc, (uint32_t)sizeof(S_RanCommMsgHdr));

    pCommMsgHdr = (S_RanCommMsgHdr *)gpRanL2L3IntfContext->sockRecvBuffer;
    switch (pCommMsgHdr->type)
    {
    case RAN_L1_API_MSG_PARAMS_REQ:
        CSP_LOG_INF(RAN_MD_L2_L3I, "RAN_L2_L3I_MsgProcess() recv RAN_L1_API_MSG_PARAMS_REQ\n");
        VRB_PUT(rc, gpRanL2Context->pL3iL1iMsgQue, pCommMsgHdr, pCommMsgHdr->size);
        break;
    case RAN_L1_API_MSG_CONFIG_REQ:
        CSP_LOG_INF(RAN_MD_L2_L3I, "RAN_L2_L3I_MsgProcess() recv RAN_L1_API_MSG_CONFIG_REQ\n");
        VRB_PUT(rc, gpRanL2Context->pL3iL1iMsgQue, pCommMsgHdr, pCommMsgHdr->size);
        break;
    case RAN_L3_L2_MSG_CONFIG_REQ:
        CSP_LOG_INF(RAN_MD_L2_L3I, "RAN_L2_L3I_MsgProcess() recv RAN_L3_L2_MSG_CONFIG_REQ\n");
        RAN_L2_L3I_ProcL3L2ConfigReq(pCommMsgHdr);
        break;
    case RAN_L1_API_MSG_START_REQ:
        CSP_LOG_INF(RAN_MD_L2_L3I, "RAN_L2_L3I_MsgProcess() recv RAN_L1_API_MSG_START_REQ\n");
        VRB_PUT(rc, gpRanL2Context->pL3iL1iMsgQue, pCommMsgHdr, pCommMsgHdr->size);
        break;
    case RAN_L3_L2_MSG_START_REQ:
        CSP_LOG_INF(RAN_MD_L2_L3I, "RAN_L2_L3I_MsgProcess() recv RAN_L3_L2_MSG_START_REQ\n");
        RAN_L2_L3I_ProcL3L2StartReq(pCommMsgHdr);
        break;
    case RAN_L3_L2_MSG_MIB_TX_REQ:
        CSP_LOG_INF(RAN_MD_L2_L3I, "RAN_L2_L3I_MsgProcess() recv RAN_L3_L2_MSG_MIB_TX_REQ\n");
        RAN_L2_L3I_ProcL3L2MibTxReq(pCommMsgHdr);
        break;
    default:
        CSP_LOG_WRN(RAN_MD_L2_L3I, "RAN_L2_L3I_MsgProcess() recv unsupport msg[%#x]\n", pCommMsgHdr->type);
        break;
    }
}

void RAN_L2_L3I_PrdProcess()
{

}

void *RAN_L2_L3I_Task(void* arg)
{
    int fd;
    uint64_t lastTimeStamp;

    fd = gpRanL2L3IntfContext->selector.recvFd;
    POLLER_Add(gpRanL2L3IntfContext->poller, fd, RAN_L2_L3I_IndProcess, NULL, NULL, NULL);

    fd = gpRanL2L3IntfContext->l3SockFd;
    POLLER_Add(gpRanL2L3IntfContext->poller, fd, RAN_L2_L3I_MsgProcess, NULL, NULL, NULL);

    while (1)
    {
        POLLER_Poll(gpRanL2L3IntfContext->poller, 5);

        if (CSP_ArrivalDelayTimeUs(&lastTimeStamp, 10))
        {
            RAN_L2_L3I_PrdProcess();
        }
        pthread_testcancel();
    }

    return NULL;
}

int32_t RAN_L2_L3I_Start(void)
{
    int32_t rc;

    rc = OS_ThreadCreate(&gpRanL2L3IntfContext->tid, RAN_L2_L3I_Task, NULL);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_L3I_Start() OS_ThreadCreate() error!\n");

    CSP_LOG_INF(RAN_MD_L2_L3I, "RAN_L2_L3I_Start() success.\n");

    return O5G_RC_OK;
}

int32_t RAN_L2_L3I_Stop(void)
{
    int32_t rc;

    rc = OS_ThreadCancel(gpRanL2L3IntfContext->tid);
    IF_RETURN(!=, O5G_RC_OK, rc, O5G_RC_ER, "RAN_L2_L3I_Stop() OS_ThreadCancel() error!\n");

    CSP_LOG_INF(RAN_MD_L2_L3I, "RAN_L2_L3I_Stop() success.\n");

    return O5G_RC_OK;
}
